package cs340omega;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

/**
 * 
 * Stores a Map of GlobalIDs to Objects and keeps it persistent by
 * reading and writing the Map to a file.
 * @author byu.ryanpadilla
 */

public class ObjectDB  implements java.io.Serializable
{
        
        private static final long serialVersionUID = 1L;
        private HashMap<GlobalID, Object> DB;
        
        /**
         * Creates a new database Map and tries to load the information from a file.
         */
        @SuppressWarnings("unchecked")
        public ObjectDB()
        {
        	DB = new HashMap<GlobalID, Object>();
                try
                {
                        FileInputStream fis = new FileInputStream("Objects.data");
                        ObjectInputStream ois = new ObjectInputStream(fis);
                        DB = new HashMap<GlobalID, Object>();
        
                        DB = DB.getClass().cast(ois.readObject());
                }
                catch (FileNotFoundException e)
                {
                        System.out.println("No Object Database file to load.");
                }
                catch (IOException e)
                {
                        System.out.println("IO Error reading Database file.");
                }
                catch (ClassNotFoundException e)
                {
                        e.printStackTrace();
                }
                catch (NullPointerException e)
                {
                        e.printStackTrace();
                        System.out.println("Continuing on.");
                }
                catch (ClassCastException e)
                {
                        System.out.println("Error reading in Database file.");
                }
        }
        /**
         * Object getter.
         * @param The Global ID of an object stored in the database.
         * @return The Object mapped to the inputed Global ID.
         */
        
        public Object get(GlobalID id)
        {
                if(DB.containsKey(id))
                {
                        return DB.get(id);
                }
                else
                {
                        return null;
                }
        }
        
        public GlobalID get(String host, int port, int localID)
        {
        	GlobalID tmp;
        	Set<GlobalID> keys = DB.keySet();
    		Iterator<GlobalID> iter = keys.iterator();
    		while (iter.hasNext())
    		{
    			tmp = iter.next();
    			if((host.compareTo(tmp.getMachineHost()) == 0 || host.compareTo(tmp.getMachineIP()) == 0)
    					&& tmp.getPort() == port && localID == tmp.getLocalID())
    			{
    				return tmp;
    			}
    		}
        
        	return null;
        }
        
        public int getHighestID(String host, int port)
        {
        	GlobalID tmp;
        	Set<GlobalID> keys = DB.keySet();
    		Iterator<GlobalID> iter = keys.iterator();
    		
    		int greatestID = -1;
    		int currentID;
    		while (iter.hasNext())
    		{
    			tmp = iter.next();    			
    			if((host.compareTo(tmp.getMachineHost()) == 0 || host.compareTo(tmp.getMachineIP()) == 0)
    					&& tmp.getPort() == port)
    			{
    				currentID = tmp.getLocalID();
    				if(currentID > greatestID)
    				{
    					greatestID = currentID;
    				}
    			}
    		}
    		return greatestID;
        	
        }
        
        public Set<GlobalID> getAllKeys()
        {
        	return DB.keySet();
        }
        /**
         * Map a new Global ID to an Object and store it in the database.
         * @param Global ID to be mapped as a key.
         * @param Object that is mapped to the inputed Global ID as a value.
         */
        public void put(GlobalID id, Object obj)
        {
                DB.put(id, obj);
        }
        /**
         * Remove the Global ID that was inputed and its corresponding Object.
         * @param Global ID to be removed.
         * @return Object that was mapped to the inputed Global ID and has now been removed
         * from the database.
         */
        public Object remove(GlobalID id)
        {
                return DB.remove(id);
        }
        /**
         * This method was theoretically suppose to run whenever the garbage collection tried to
         * destroy the database, but that proved fruitless.  So this method is called explicitly
         * to store the database to a file.
         */
        protected void finalize()
        {
                try
                {
                	super.finalize();
                	System.out.println("Writing Database to file.");
                	File objects = new File("Objects.data");
                	objects.delete();
                	FileOutputStream fos = new FileOutputStream("Objects.data");
                	ObjectOutputStream oos = new ObjectOutputStream(fos);
                
                	oos.writeObject(DB);
                	oos.close();
                }
                catch (IOException e)
                {
                	e.printStackTrace();
                	System.out.println("IO Error writing Database file.");
                }
                catch (NullPointerException e)
                {
                	e.printStackTrace();
                }
                catch (Throwable e) 
                {
                	e.printStackTrace();
                }
        }
        /**
         * @return String representation of the Contents of the database.  The Database Keys
         * (the Global ID) are all printed out first, and then all the Values (Object) are printed
         * out.
         */
        		
        public String toString()
        {
        	String result = "Database Contains:";
        	if (DB != null && DB.size() > 0)
        	{
        		for (GlobalID obj : DB.keySet())
        		{
        			result += "\n" + obj.toString();        		
        		}
        		for (Object obj : DB.values())
        		{
        			result += "\n" + obj.toString();        			
        		}        	
        	}
        	else
        	{
        		result += "EMPTY!";
        	}
        	
        	return result;
        }
}

